<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8>
    <title>Chapter&nbsp;3.&nbsp;教程</title>
    <link rel="stylesheet" href="css/html.css" type="text/css">
    <meta name="generator" content="DocBook XSL Stylesheets V1.65.1">
    <link rel="home" href="index.html" title="JBoss jBPM 3.1">
    <link rel="up" href="index.html" title="JBoss jBPM 3.1">
    <link rel="previous" href="gettingstarted.html" title="Chapter&nbsp;2.&nbsp;Getting started">
    <link rel="next" href="graphorientedprogramming.html" title="Chapter&nbsp;4.&nbsp;Graph Oriented Programming">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<div class="navheader">
    <table width="100%" summary="Navigation header">
        <tr>
            <th colspan="3" align="center">Chapter&nbsp;3.&nbsp;教程</th>
        </tr>
        <tr>
            <td width="20%" align="left"><a accesskey="p" href="gettingstarted.html">Prev</a>&nbsp;</td>
            <th width="60%" align="center">&nbsp;</th>
            <td width="20%" align="right">&nbsp;<a accesskey="n" href="graphorientedprogramming.html">Next</a></td>
        </tr>
    </table>
    <hr>
</div>
<div class="chapter" lang="cn">
<div class="titlepage">
    <div>
        <div><h2 class="title"><a name="tutorial"></a>Chapter&nbsp;3.&nbsp;教程</h2></div>
    </div>
    <div></div>
</div>
<p> 这个教程会向你展示jpdl中的基本流程和管理运行时执行的API。</p>

<p> 这个教程解释一组例子。这些例子围绕着某一主题，并含有大量的说明。例子源代码可以在
    <tt class="literal">src/java.examples</tt>目录中找到。</p>

<p> 学习这些例子最好的方法是创建一个项目，试验所有这些例子，并更改其中一些内容然后观察结果。</p>

<p> eclipse用户要开始这个教程，可以下载 jbpm-3.0-[version].zip 并解压到你的系统中。跟着选择菜单"File" --&gt;
    "Import..." --&gt; "Existing Project into Workspace"。点击"Next"，选择jBPM根目录然后点击"Finish"。这样你的workspace中就
    有了一个jbpm.3项目。你可以在<tt class="literal">src/java.examples/...</tt>中找到教程的例子源代码。当你打开这些例子的时候，
    可以通过"Run" --&gt;"Run As..." --&gt; "JUnit Test"菜单来测试运行它们。
</p>

<p> jBPM包含了一个图形化流程设计器，可以用来创建例子中的这些流程的XML定义文件。可以在
    <a href="gettingstarted.html#downloadablesoverview" title="2.1.&nbsp;可下载程序包概览">
        Section&nbsp;2.1, &#8220;Downloadables Overview&#8221;</a>
    中找到如何使用该工具的说明。你不需要通过流程设计器来完成本教程。
</p>

<p>State machines can be
</p>

<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="helloworldexample"></a>3.1.&nbsp;Hello World 例子
            </h2></div>
        </div>
        <div></div>
    </div>
    <p> 流程定义是一个有向图，它由节点（nodes）和迁移（transitions）组成。Hello World流程有3个节点。要看到这些东西是怎么结合在一起的，
        我们准备不用流程设计器，而是通过一个简单流程来开始。下图展示了hello world流程：
    </p>

    <div class="figure"><a name="hello.world.image"></a>

        <div class="mediaobject" align="center"><img src="images/hello.world.gif" align="middle"
                                                     alt="The hello world process graph"></div>
        <p class="title"><b>Figure&nbsp;3.1.&nbsp;hello world 流程图</b></p></div><pre
        class="programlisting">public void testHelloWorldProcess() {
  <span class="bold"><b>/* 本方法展示了一个流程定义和对该流程的一次执行。该流程定义有3个节点：
      一个没有命名的start-state状态节点，一个‘s’状态节点和一个end-state状态节点。
      下一行把一小段xml文本解释到ProcessDefinition流程定义对象中。
      ProcessDefinition是流程的java对象表示。*/</b></span>
  ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
    "<span class="bold"><b>&lt;process-definition&gt;</b></span>" +
    "<span class="bold"><b>  &lt;start-state&gt;</b></span>" +
    "<span class="bold"><b>    &lt;transition to='s' /&gt;</b></span>" +
    "<span class="bold"><b>  &lt;/start-state&gt;</b></span>" +
    "<span class="bold"><b>  &lt;state name='s'&gt;</b></span>" +
    "<span class="bold"><b>    &lt;transition to='end' /&gt;</b></span>" +
    "<span class="bold"><b>  &lt;/state&gt;</b></span>" +
    "<span class="bold"><b>  &lt;end-state name='end' /&gt;</b></span>" +
    "<span class="bold"><b>&lt;/process-definition&gt;</b></span>"
  );
  
  <span class="bold"><b>// 下一行为流程定义创建一个执行。</b></span>
  <span class="bold"><b>// 构建后，流程有一条主执行（execution）路径（=根token）定位在开始节点（start-state）上.</b></span>
  ProcessInstance processInstance = 
      new ProcessInstance(processDefinition);
  
  <span class="bold"><b>// 流程实例构建后，有一条主执行路径，即根token(root token)</b></span>
  Token token = processInstance.getRootToken();
  
  <span class="bold"><b>// 流程实例构建后，主执行路径被定位在流程定义的开始节点（start-state）上。</b></span>
  assertSame(processDefinition.getStartState(), token.getNode());
  
  <span class="bold"><b>// 好了，让我们开始流程的执行，通过开始节点的缺省迁移路径离开开始节点。</b></span>
  token.signal();
  <span class="bold"><b>// signal方法在流程执行进入一个等待状态（wait state）前会被一直被堵塞。</b></span>

  <span class="bold"><b>// 在's'状态节点中，流程执行将已进入第一个等待状态，所以执行的主路径现在指向状态节点's'</b></span>
  assertSame(processDefinition.getNode("s"), token.getNode());

  <span class="bold"><b>// 让我们发出另外一个信号（signal）。通过's'节点的缺省迁移路径离开状态节点's'，回到执行中。</b></span>
  token.signal();
  <span class="bold"><b>// 现在，因为流程实例已经到达结束节点（end-statte），signal方法返回。</b></span>
  
  assertSame(processDefinition.getNode("end"), token.getNode());
}</pre>
</div>
<div class="section" lang="cn">
<div class="titlepage">
    <div>
        <div><h2 class="title" style="clear: both"><a name="databaseexample"></a>3.2.&nbsp;数据库版本的例子</h2></div>
    </div>
    <div></div>
</div>
<p> jBPM的一个基本特性是具有当它们处于等待状态的时候，持久化流程执行信息到数据库中的能力。下一个例子会展示给你看如何存储一个流程实例信息
    到jBPM数据库中。当这个操作发生的时候，该例子也建议同时存储上下文的信息（ The example also suggests a context in
    which this might occur.）对不同的用户代码块分别创建不同的方法。例如：有一块在web引用中的用户代码启动一个流程，同时持久化execution
    到数据库。稍后，一个消息驱动bean从数据库装载该流程实例，并恢复该execution。
</p>

<p> 关于jBPM数据持久化的信息，可以在<a href="persistence.html" title="Chapter&nbsp;7.&nbsp;数据持久化">Chapter&nbsp;7,
    <i>数据持久化</i></a>中找到。</p>
<pre class="programlisting">public class HelloWorldDbTest extends TestCase {

  static JbpmConfiguration jbpmConfiguration = null; 

  static {
    <span class="bold"><b>/* 一个类似的配置文件例子可以在'src/config.files'中找到。通常这些配置信息在资源文件'jbpm.cfg.xml'
        中，但是在这里我们通过一个xml字符串传入这些配置信息。</b></span>
    <span class="bold"><b>首先，我们静态创建一个JbpmConfiguration对象，该对象可以在系统中所有的线程使用，因此我们可以创建为静态变量。*/</b></span>

    jbpmConfiguration = JbpmConfiguration.parseXmlString(
      "&lt;jbpm-configuration&gt;" +
      
      <span class="bold"><b>// jbpm-context机制把jbpm核心引擎从可能用到的环境提供的服务中分离出来。</b></span>
      
      "  &lt;jbpm-context&gt;" +
      "    &lt;service name='persistence' " +
      "             factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' /&gt;" + 
      "  &lt;/jbpm-context&gt;" +
      
      <span class="bold"><b>// 所有jbpm用到的资源文件也是在jbpm.cfg.xml中引用</b></span>
      
      "  &lt;string name='resource.hibernate.cfg.xml' " +
      "          value='hibernate.cfg.xml' /&gt;" +
      "  &lt;string name='resource.business.calendar' " +
      "          value='org/jbpm/calendar/jbpm.business.calendar.properties' /&gt;" +
      "  &lt;string name='resource.default.modules' " +
      "          value='org/jbpm/graph/def/jbpm.default.modules.properties' /&gt;" +
      "  &lt;string name='resource.converter' " +
      "          value='org/jbpm/db/hibernate/jbpm.converter.properties' /&gt;" +
      "  &lt;string name='resource.action.types' " +
      "          value='org/jbpm/graph/action/action.types.xml' /&gt;" +
      "  &lt;string name='resource.node.types' " +
      "          value='org/jbpm/graph/node/node.types.xml' /&gt;" +
      "  &lt;string name='resource.varmapping' " +
      "          value='org/jbpm/context/exe/jbpm.varmapping.xml' /&gt;" +
      "&lt;/jbpm-configuration&gt;"
    );
  }
  
  public void setUp() {
    jbpmConfiguration.createSchema();
  }
  
  public void tearDown() {
    jbpmConfiguration.dropSchema();
  }

  public void testSimplePersistence() {
    <span class="bold"><b>/* 在下面3个方法的调用中，所有数据是通过数据库来传递的。在本单元测试中，
        这3个方法依次序执行，因为我们想要测试一个完整的流程过程场景。但在现实世界中，这些方法表示是对
        服务器发出的不同的请求。*/</b></span>
    
    <span class="bold"><b>// 由于我们是从一个干净、全空的内存数据库开始的，我们还得先部署流程。
        // 现实世界中，这是流程开发者一次完成的工作。</b></span>
    deployProcessDefinition();

    <span class="bold"><b>// 假设我们希望当用户在web应用中提交form的时候启动一个流程实例...</b></span>
    processInstanceIsCreatedWhenUserSubmitsWebappForm();

    <span class="bold"><b>// 接着，稍后，当异步信息到达的时候，execution必须继续。</b></span>
    theProcessInstanceContinuesWhenAnAsyncMessageIsReceived();
  }

  public void deployProcessDefinition() {
    <span class="bold"><b>// 本测试展示一个流程定义和流程定义的一个execution。该流程定义有3个节点：
        一个没有命名的开始节点（start-state），一个状态节点's'，和一个命名为'end'的结束节点（end-state）。</b></span>
    ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
      "&lt;process-definition name='hello world'&gt;" +
      "  &lt;start-state name='start'&gt;" +
      "    &lt;transition to='s' /&gt;" +
      "  &lt;/start-state&gt;" +
      "  &lt;state name='s'&gt;" +
      "    &lt;transition to='end' /&gt;" +
      "  &lt;/state&gt;" +
      "  &lt;end-state name='end' /&gt;" +
      "&lt;/process-definition&gt;"
    );

    <span class="bold"><b>// 查找上面配置好的pojo数据持久化上下文对象</b></span>
    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    try {
      <span class="bold"><b>// Deploy the process definition in the database </b></span>
      jbpmContext.deployProcessDefinition(processDefinition);

    } finally {
      <span class="bold"><b>// 卸掉pojo数据持久化上下文对象.</b></span>
      <span class="bold"><b>// 这包括运行flush使得流程定义数据插入数据库的SQL都能跑完。</b></span>
      jbpmContext.close();
    }
  }

  public void processInstanceIsCreatedWhenUserSubmitsWebappForm() {
    <span class="bold"><b>// 这里的代码也可以用在一个struts-action或JSF的管理bean中。</b></span>

    <span class="bold"><b>// 查找上面配置好的pojo数据持久化上下文对象</b></span>
    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    try {

      GraphSession graphSession = jbpmContext.getGraphSession();
      
      ProcessDefinition processDefinition = 
          graphSession.findLatestProcessDefinition("hello world");
    
      <span class="bold"><b>// 利用从数据库中检索到的processDefinition流程定义对象，我们可以创建一个该流程定义的execution，
          就如我们在hello world例子中做的那样（那会儿没有数据持久化）。</b></span>
      ProcessInstance processInstance = 
          new ProcessInstance(processDefinition);
      
      Token token = processInstance.getRootToken(); 
      assertEquals("start", token.getNode().getName());
      <span class="bold"><b>// 让我们启动流程的执行</b></span>
      token.signal();
      <span class="bold"><b>// 现在流程来到状态节点's'.</b></span>
      assertEquals("s", token.getNode().getName());
      
      <span class="bold"><b>// 现在，processInstance流程实例对象已经被保存在数据库中。所以当前流程的执行的状态也保存在数据库里了。</b></span>
      jbpmContext.save(processInstance);
      <span class="bold"><b>// 下面的方法从数据库取回该流程实例，并通过另外提供另外一个外部信号恢复流程执行。</b></span>

    } finally {
      <span class="bold"><b>// 卸掉数据持久化上下文对象</b></span>
      jbpmContext.close();
    }
  }

  public void theProcessInstanceContinuesWhenAnAsyncMessageIsReceived() {
    <span class="bold"><b>// 本方法中的代码，可能是被用在一个消息驱动bean中的代码</b></span>

    <span class="bold"><b>// 找到jbpmContext</b></span>
    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    try {

      GraphSession graphSession = jbpmContext.getGraphSession();
      <span class="bold"><b>// 先从数据库找回流程定义对象。</b></span>
      <span class="bold"><b>// 有几个选项以便知道哪个流程实例是我们这里正在处理的,在这个简单测试中，
          最容易的方法是取得一个流程实例的完整列表。那应该只给我们返回一个结果，所以让我们先找到流程定义对象。</b></span>
      
      ProcessDefinition processDefinition = 
          graphSession.findLatestProcessDefinition("hello world");

      <span class="bold"><b>// 现在，我们搜索这个流程定义的所有流程实例</b></span>
      List processInstances = 
          graphSession.findProcessInstances(processDefinition.getId());
      
      <span class="bold"><b>// 因为我们知道在这个单元测试的上下文中，只有一个执行对象。现实世界中，流程实例的processInstanceId
          可以从到达的信息中获取，或由用户的选择来决定。</b></span>
      ProcessInstance processInstance = 
          (ProcessInstance) processInstances.get(0);
      
      <span class="bold"><b>// 现在我们可以继续流程的执行了。注意流程实例对象processInstance委派信号给主执行路径。(=the root token).</b></span>
      processInstance.signal();

      <span class="bold"><b>// 发出信号后，我们知道流程执行会到达结束节点（end-state）</b></span>
      assertTrue(processInstance.hasEnded());
      
      <span class="bold"><b>// 现在我们可以更新流程执行的状态到数据库中了。</b></span>
      jbpmContext.save(processInstance);

    } finally {
      <span class="bold"><b>// 关闭数据持久化上下文对象jbpmContext</b></span>
      jbpmContext.close();
    }
  }
}</pre>
</div>
<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="d0e640"></a>3.3.&nbsp;上下文的例子：流程变量
            </h2></div>
        </div>
        <div></div>
    </div>
    <p> 流程变量包含流程执行过程中的上下文信息。它与<tt class="literal">java.util.Map</tt>非常相似，
        可以映射一个名值对，名称和值都是java对象。流程变量作为流程实例的一部分被持久化。为了保持事情的简单
        性，在这个例子中我们仅仅展示与流程变量一起工作的API，而不涉及持久化。</p>

    <p>要获的更多关于流程变量的信息，可以在<a href="context.html" title="Chapter&nbsp;10.&nbsp;Context">Chapter&nbsp;10,
        <i>上下文（Context）</i></a>中找到。</p>
    <pre class="programlisting"><span class="bold"><b>// 这个例子也是从hello world例子开始的</b></span>
<span class="bold"><b>// 这次甚至没有更改。</b></span>
ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
  "&lt;process-definition&gt;" +
  "  &lt;start-state&gt;" +
  "    &lt;transition to='s' /&gt;" +
  "  &lt;/start-state&gt;" +
  "  &lt;state name='s'&gt;" +
  "    &lt;transition to='end' /&gt;" +
  "  &lt;/state&gt;" +
  "  &lt;end-state name='end' /&gt;" +
  "&lt;/process-definition&gt;"
);

ProcessInstance processInstance =
  new ProcessInstance(processDefinition);

<span class="bold"><b>// 从流程实例对象获取上下文实例对象，以和流程变量一起工作</b></span>
ContextInstance contextInstance = 
  processInstance.getContextInstance();

<span class="bold"><b>// 在流程离开开始节点之前，我们准备设置一些流程变量到流程实例的上下文中。</b></span>
contextInstance.setVariable("amount", new Integer(500));
contextInstance.setVariable("reason", "i met my deadline");

<span class="bold"><b>// 从现在开始，这些变量就和流程实例关联在一起了。这些变量现在可以被用户代码通
    过这里展示的API存取，在动作（action）和节点（node）实现中也可以访问。
    这些流程变量也可以存储到数据库中，作为流程实例的一部分。</b></span>

processInstance.signal();

<span class="bold"><b>// 这些变量可以通过流程上下文对象contextInstance访问. </b></span>

assertEquals(new Integer(500), 
             contextInstance.getVariable("amount"));
assertEquals("i met my deadline", 
             contextInstance.getVariable("reason"));</pre>
</div>
<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="taskassignmentexample"></a>3.4.&nbsp;任务指派（Task assignment）例子
                </h2></div>
        </div>
        <div></div>
    </div>
    <p> 在下一个例子中，我们将展示如何才能把一项任务指派给一个用户。由于jBPM流程引擎和组织模型之间的
        分离关系，使用一个表达式（expression）来计算角色总是太有限了。因此你不得不指定一个
        AssignmentHandler接口的实现类来一起处理任务角色的计算。
    </p><pre class="programlisting">public void testTaskAssignment() {
  <span class="bold"><b>// 下面的流程是基于hello world流程的。</b></span>
  <span class="bold"><b>// 状态节点被用一个任务节点（task-node）来替换了。
      任务节点是JPDL中表示一个等待状态和产生任务的节点，产生的任务要在流程继续执行前被完成。</b></span>
  ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
    "<span class="bold"><b>&lt;process-definition name='the baby process'&gt;"</b></span> +
    "<span class="bold"><b>  &lt;start-state&gt;"</b></span> +
    "<span class="bold"><b>    &lt;transition name='baby cries' to='t' /&gt;"</b></span> +
    "<span class="bold"><b>  &lt;/start-state&gt;"</b></span> +
    "<span class="bold"><b>  &lt;task-node name='t'&gt;"</b></span> +
    "<span class="bold"><b>    &lt;task name='change nappy'&gt;"</b></span> +
    "<span class="bold"><b>      &lt;assignment class='org.jbpm.tutorial.taskmgmt.NappyAssignmentHandler'
    /&gt;"</b></span> +
    "<span class="bold"><b>    &lt;/task&gt;"</b></span> +
    "<span class="bold"><b>    &lt;transition to='end' /&gt;"</b></span> +
    "<span class="bold"><b>  &lt;/task-node&gt;"</b></span> +
    "<span class="bold"><b>  &lt;end-state name='end' /&gt;"</b></span> +
    "<span class="bold"><b>&lt;/process-definition&gt;"</b></span>
  );
  
  <span class="bold"><b>// 为流程定义创建一个执行.</b></span>
  ProcessInstance processInstance = 
      new ProcessInstance(processDefinition);
  Token token = processInstance.getRootToken();
  
  <span class="bold"><b>// 让我们开始执行流程，通过缺省迁移路径离开开始节点</b></span>
  token.signal();
  <span class="bold"><b>// signal方法在流程进入等待状态前一直被阻塞. </b></span>
  <span class="bold"><b>// 在这个例子中，是一个任务节点.</b></span>
  assertSame(processDefinition.getNode("t"), token.getNode());

  <span class="bold"><b>// 当执行到达任务节点，一个任务'change nappy'被创建出来，</b></span>
  <span class="bold"><b>// 同时NappyAssignmentHandler会被呼叫以决定该任务指派给谁。</b></span>
  <span class="bold"><b>// NappyAssignmentHandler 返回'papa'</b></span>

  <span class="bold"><b>// 在一个真实的环境中，任务应该是用org.jbpm.db.TaskMgmtSession中的方法从数据库中取得的。</b></span>
  <span class="bold"><b>// 因为我们不想包含持久化的复杂性在这个例子中，我们只是取流程实例对象的第一个任务实例对象。</b></span>
  <span class="bold"><b>// （我们知道在这个测试场景中只会有一个任务实例对象） </b></span>
  TaskInstance taskInstance = (TaskInstance)
      processInstance
        .getTaskMgmtInstance()
        .getTaskInstances()
        .iterator().next();

  <span class="bold"><b>// 现在，我们检查任务实例对象taskInstance是否已经指派给了'papa'.</b></span>
  assertEquals("papa", taskInstance.getActorId() );
  
  <span class="bold"><b>// 现在我们假设'papa'已经完成了他的任务并标识任务为完成状态。</b></span>
  taskInstance.end();
  <span class="bold"><b>// 由于这是最后一个（也是唯一一个）要做的任务，该项任务的完成会技法流程实例继续执行。</b></span>
  
  assertSame(processDefinition.getNode("end"), token.getNode());
}</pre>
</div>
<div class="section" lang="cn">
<div class="titlepage">
    <div>
        <div><h2 class="title" style="clear: both"><a name="customactionexample"></a>3.5.&nbsp;定制动作（action）例子
        </h2></div>
    </div>
    <div></div>
</div>
<p> 动作是一个绑定你定制的java代码到jBPM流程中的机制。动作可以与它自己的节点关联（如果他们在流程图上相关）。
    它也可以被放到事件（events）中，例如：选择一条迁移路径，离开一个节点或进入一个节点。在这个例子中，动作
    不是流程图的一部分，但当执行在流程运行时触发一个事件的时候会被运行。</p>

<p> 我们先来看一下准备在本例子中用到的动作实现类<tt class="literal">MyActionHandler</tt>。这个动作
    处理器实现不做任何真正引人入胜的事情...它只是设置了一个boolean变量<tt class="literal">isExecuted</tt>
    为<tt class="literal">true</tt>。该变量<tt class="literal">isExecuted</tt>是静态变量，所以能被动作
    处理器中的代码访问到。
</p>

<p> 更多信息请看<a href="processmodelling.html#actions" title="9.5.&nbsp;Actions">Section&nbsp;9.5,
    &#8220;动作（Actions）&#8221;</a></p><pre class="programlisting">
    <span class="bold"><b>// MyActionHandler 是一个会在jBPM流程执行的期间运行某些用户代码的类。</b></span>
public class MyActionHandler implements ActionHandler {

  <span class="bold"><b>// 在每一个测试之前(即在setUp方法中), 设置 isExecuted 的值为false </b></span>
  public static boolean isExecuted = false;

  <span class="bold"><b>// 该动作会设置isExecuted为true使得单元测试能判断其是否被运行过了。</b></span>
  public void execute(ExecutionContext executionContext) {
    isExecuted = true;
  }
}</pre>
<p>跟之前一样，在每一个测试之前(即在setUp方法中), 设置 <tt class="literal">MyActionHandler.isExecuted</tt> 的值为false
    <pre class="programlisting">  <span
        class="bold"><b>// 每个测试开始前会设置 MyActionHandler.isExecuted 为false</b></span>
  public void setUp() {
    MyActionHandler.isExecuted = false;
  }</pre>
<p>我们将在一个迁移路径上使用动作。</p>
<pre class="programlisting">public void testTransitionAction() {
    <span class="bold"><b>// 下一个流程是hello world流程的变种。</b></span>
    <span class="bold"><b>// 我们已经在从状态节点's'到结束节点的迁移路径上添加了一个动作。</b></span>
    <span class="bold"><b>// 这个测试的目的是展示在jBPM流程中集成用户代码是多么的容易。</b></span>
    ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
      "<span class="bold"><b>&lt;process-definition&gt;</b></span>" +
      "<span class="bold"><b>  &lt;start-state&gt;</b></span>" +
      "<span class="bold"><b>    &lt;transition to='s' /&gt;</b></span>" +
      "<span class="bold"><b>  &lt;/start-state&gt;</b></span>" +
      "<span class="bold"><b>  &lt;state name='s'&gt;</b></span>" +
      "<span class="bold"><b>    &lt;transition to='end'&gt;</b></span>" +
      "<span class="bold"><b>      &lt;action class='org.jbpm.tutorial.action.MyActionHandler' /&gt;</b></span>" +
      "<span class="bold"><b>    &lt;/transition&gt;</b></span>" +
      "<span class="bold"><b>  &lt;/state&gt;</b></span>" +
      "<span class="bold"><b>  &lt;end-state name='end' /&gt;</b></span>" +
      "<span class="bold"><b>&lt;/process-definition&gt;</b></span>"
    );
    
    <span class="bold"><b>// 让我们为流程定义启动一个新的执行。</b></span>
    ProcessInstance processInstance = 
      new ProcessInstance(processDefinition);
    
    <span class="bold"><b>// 下一个signal会引致执行从开始节点迁移到状态节点's'</b></span>
    processInstance.signal();

    <span class="bold"><b>// 这里我们展示MyActionHandler还没被运行.</b></span>
    assertFalse(MyActionHandler.isExecuted);
    <span class="bold"><b>// ... 以及主执行路径被指向到状态节点's'</b></span>
    assertSame(processDefinition.getNode("s"), 
               processInstance.getRootToken().getNode());
    
    <span class="bold"><b>// 下一个signal会触发root token的执行。 </b></span>
    <span class="bold"><b>// 该token会选择带有动作的迁移路径，在调用signal方法期间动作将会被运行。</b></span>
    processInstance.signal();
    
    <span class="bold"><b>// 这里我们可以看到 MyActionHandler 已经在signal调用的过程中被运行了。</b></span>
    assertTrue(MyActionHandler.isExecuted);
  }</pre>
<p>下一个例子展示同样的动作，但是现在这个动作分别放在 <tt class="literal">enter-node</tt>
    和 <tt class="literal">leave-node</tt> 事件中。
    注意与迁移不同，一个节点有多个事件类型，而迁移路径只有一个事件。因此动作放在节点应该置于事件元素中。
</p><pre class="programlisting">ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
  "&lt;process-definition&gt;" +
  "  &lt;start-state&gt;" +
  "    &lt;transition to='s' /&gt;" +
  "  &lt;/start-state&gt;" +
  "  &lt;state name='s'&gt;" +
  "<span class="bold"><b>    &lt;event type='node-enter'&gt;</b></span>" +
  "<span class="bold"><b>      &lt;action class='org.jbpm.tutorial.action.MyActionHandler' /&gt;</b></span>" +
  "<span class="bold"><b>    &lt;/event&gt;</b></span>" +
  "<span class="bold"><b>    &lt;event type='node-leave'&gt;</b></span>" +
  "<span class="bold"><b>      &lt;action class='org.jbpm.tutorial.action.MyActionHandler' /&gt;</b></span>" +
  "<span class="bold"><b>    &lt;/event&gt;</b></span>" +
  "    &lt;transition to='end'/&gt;" +
  "  &lt;/state&gt;" +
  "  &lt;end-state name='end' /&gt;" +
  "&lt;/process-definition&gt;"
);

ProcessInstance processInstance = 
  new ProcessInstance(processDefinition);

assertFalse(MyActionHandler.isExecuted);
<span class="bold"><b>// 下一个signal会引起从开始节点到状态节点's'的迁移。所以会进入状态节点‘s’，因此动作会被执行。</b></span>
processInstance.signal();
assertTrue(MyActionHandler.isExecuted);

<span class="bold"><b>// 让我们重设一下MyActionHandler.isExecuted的值为false </b></span>
MyActionHandler.isExecuted = false;

<span class="bold"><b>// 下一个signal会触发执行离开状态节点's'的迁移，所以动作会再次被执行. </b></span>
processInstance.signal();
<span class="bold"><b>// Voila. </b></span>
assertTrue(MyActionHandler.isExecuted);
</pre>
</div>
</div>
<div class="navfooter">
    <hr>
    <table width="100%" summary="Navigation footer">
        <tr>
            <td width="40%" align="left"><a accesskey="p" href="gettingstarted.html">Prev</a>&nbsp;</td>
            <td width="20%" align="center"><a accesskey="u" href="index.html">Up</a></td>
            <td width="40%" align="right">&nbsp;<a accesskey="n" href="graphorientedprogramming.html">Next</a></td>
        </tr>
        <tr>
            <td width="40%" align="left" valign="top">Chapter&nbsp;2.&nbsp;起步&nbsp;</td>
            <td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td>
            <td width="40%" align="right" valign="top">&nbsp;Chapter&nbsp;4.&nbsp;面向图的程序设计</td>
        </tr>
    </table>
</div>
</body>
</html>